home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Tool Chest / Dev.CD Feb 97 TC.toast / Sample Code / Interapplication Communication / MenuScripter 4.0 / Sources / MSDrag.c < prev    next >
Encoding:
Text File  |  1996-07-09  |  26.3 KB  |  1,117 lines  |  [TEXT/CWIE]

  1. // SVDrag.c
  2. //
  3. // Original version by Jon Lansdell and Nigel Humphreys.
  4. // 4.0 and 3.1 updates by Greg Sutton.
  5. // Drag Manager support by Chris White
  6. // ©Apple Computer Inc 1996, all rights reserved.
  7.  
  8. #include "MSDrag.h"
  9. #include "MSWindow.h"
  10. #include "MSGlobals.h"
  11. #include "Offscreen.h"
  12.  
  13. #include "DebugUtils.h"
  14.  
  15.  
  16. #include <Drag.h>
  17. #include <LowMem.h>
  18. #include <Errors.h>
  19. #include <Folders.h>
  20.  
  21.  
  22.  
  23. static short    gCaretOffset;        // Caret drawn during a drag
  24.  
  25.  
  26.  
  27. #pragma segment Drag
  28.  
  29. //
  30. // InitDragHandlers creates the UPPs for the Drag Manager 
  31. // callback routines _if_ the Drag Manager is available.
  32. //
  33. OSErr InitDragHandlers ( void )
  34. {
  35.     OSErr    theErr = noErr;
  36.     
  37.     if ( gHasDragManager )
  38.     {
  39.         gDragTrackingHandlerUPP = NewDragTrackingHandlerProc ( MyTrackingHandler );
  40.         gDragReceiveHandlerUPP = NewDragReceiveHandlerProc ( MyReceiveHandler );
  41.     }
  42.     
  43.     return theErr;
  44. }
  45.  
  46.  
  47.  
  48. //
  49. // InstallDragHandlers attaches the tracking and receive handlers to
  50. // one of the application's windows.
  51. //
  52. OSErr InstallDragHandlers ( WindowRef theWindow )
  53. {    
  54.     OSErr     theErr = noErr;
  55.     
  56.     if ( gHasDragManager )
  57.     {
  58.         
  59.         theErr = InstallTrackingHandler ( gDragTrackingHandlerUPP, theWindow, nil );
  60.     
  61.         if ( theErr == noErr )
  62.         {
  63.             theErr = InstallReceiveHandler ( gDragReceiveHandlerUPP, theWindow, nil );
  64.             if ( theErr )
  65.                 (void) RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow );
  66.         }
  67.         
  68.     }
  69.     
  70.     return theErr;
  71. }
  72.  
  73.  
  74.  
  75. //
  76. // RemoveDragHandlers removes the tracking and receive handlers from
  77. // one of the application's windows (usually just prior to disposal).
  78. //
  79. void RemoveDragHandlers ( WindowRef theWindow )
  80. {
  81.     if ( gHasDragManager )
  82.     {
  83.         
  84.         RemoveReceiveHandler ( gDragReceiveHandlerUPP, theWindow );
  85.         RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow );
  86.         
  87.     }
  88.     
  89.     return;
  90. }
  91.  
  92.  
  93.  
  94. Boolean IsDragInWindowContent ( DragReference theDrag, WindowRef theWindow )
  95. {
  96.     Point    localPt;
  97.     
  98.     GetDragMouse ( theDrag, &localPt, 0L );
  99.     GlobalToLocal ( &localPt );            // Assumes theWindow is current port
  100.     
  101.     return PtInWindow ( localPt, theWindow );
  102. }
  103.  
  104.  
  105.  
  106. Boolean PtInWindow ( Point localPt, WindowRef theWindow )
  107. {
  108.     DPtr theDocument;
  109.     
  110.     theDocument = DPtrFromWindowPtr ( theWindow );
  111.     return PtInDocument ( localPt, theDocument );
  112. }
  113.  
  114.  
  115.  
  116. Boolean PtInDocument ( Point localPt, DPtr theDocument )
  117. {
  118.     return PtInRect ( localPt, &(**(theDocument->theText)).viewRect );
  119. }
  120.  
  121.  
  122. Boolean CanAcceptDragItems ( DragReference theDrag, WindowPtr theWindow )
  123. {
  124.     OSErr            theErr;
  125.     ItemReference    theRef;
  126.     Boolean            bCanAccept = false;
  127.     HFSFlavor         currHFSFlavor;
  128.     Size            flavorDataSize;
  129.     FlavorFlags        currFlavorFlags;
  130.     DPtr            theDocument;
  131.     
  132.     theDocument = DPtrFromWindowPtr ( theWindow );
  133.  
  134.     if (theDocument->windowType == kOrdinaryWind)
  135.     {
  136.         theErr = GetDragItemReferenceNumber ( theDrag, 1, &theRef );
  137.         if ( theErr == noErr )
  138.         {
  139.             // use GetFlavorFlags to check on flavor existence of TEXT data.
  140.             theErr = GetFlavorFlags ( theDrag, theRef, 'TEXT', &currFlavorFlags );
  141.             if ( theErr == noErr )
  142.                 bCanAccept = true;
  143.             else
  144.             {
  145.                 // check if the item is a file spec, and it contains TEXT
  146.                 flavorDataSize = sizeof ( HFSFlavor );
  147.                 theErr = GetFlavorData ( theDrag, theRef, flavorTypeHFS, &currHFSFlavor,
  148.                                                 &flavorDataSize, 0    );
  149.                 
  150.                 if ( theErr == noErr && currHFSFlavor.fileType == 'TEXT' ) 
  151.                     bCanAccept = true;
  152.             }
  153.         }
  154.     }                    
  155.     
  156.     return (bCanAccept);
  157. }
  158.  
  159.  
  160.  
  161. pascal OSErr MyTrackingHandler ( DragTrackingMessage theMessage, WindowPtr theWindow,
  162.                                     void* handlerRefCon, DragReference theDrag )
  163. {
  164. #ifdef __MWERKS__
  165.     #pragma unused(handlerRefCon)
  166. #endif
  167.  
  168.     static Boolean    bHasAcceptableDrag;
  169.     static Boolean    bHasHilitedWindow;
  170.     static Boolean    bShowCaret;
  171.     
  172.     static long        caretMovedTime;
  173.     static short    lastOffset, insertPosition;
  174.     
  175.     short            theOffset;
  176.     OSErr            theErr = noErr;
  177.     long            theTime = TickCount ( );
  178.     unsigned long    theAttributes;
  179.     RgnHandle        tempRgn;
  180.     DPtr            theDocument, hitDocument;
  181.     
  182.     
  183.     GetDragAttributes ( theDrag, &theAttributes );
  184.     theDocument = DPtrFromWindowPtr ( theWindow );
  185.     
  186.     switch ( theMessage )
  187.     {
  188.         case dragTrackingEnterHandler:
  189.             // Any initialization for this window handler.
  190.             
  191.             bHasAcceptableDrag = CanAcceptDragItems ( theDrag, theWindow );
  192.             
  193.             // Let the drag manager know if we can't accept this drag
  194.             if ( !bHasAcceptableDrag )
  195.                 theErr = dragNotAcceptedErr;
  196.         break;
  197.             
  198.         case dragTrackingEnterWindow: 
  199.             
  200.             caretMovedTime = theTime;
  201.             gCaretOffset = lastOffset = -1;
  202.             bShowCaret = true;
  203.  
  204.             bHasHilitedWindow = false;
  205.             
  206.         break;
  207.         
  208.         case dragTrackingInWindow:
  209.             // Hiliting of the window during a drag is done
  210.             // here.  Do it only if we can accept these items
  211.             // and we're not in the source window.
  212.             
  213.             if ( bHasAcceptableDrag )
  214.             {
  215.                 // Check if the mouse is in a window's content region. Make an
  216.                 // exception if the window has yet to leave the source window,
  217.                 // since we don't want to hilite it in that stuation.
  218.                 Boolean        bMouseInContent;
  219.                 Point        theMouse;
  220.                 
  221.                 bMouseInContent = false;
  222.  
  223.                 if ( theAttributes & dragHasLeftSenderWindow )
  224.                 {
  225.                     bMouseInContent = IsDragInWindowContent ( theDrag, theWindow );
  226.                 }
  227.                 
  228.                 // If the mouse is in a window and it isn't hilited...
  229.                 if ( bMouseInContent && !bHasHilitedWindow )
  230.                 {
  231.                     tempRgn = NewRgn ( );
  232.                     RectRgn ( tempRgn, &(**(theDocument->theText)).viewRect );
  233.                     
  234.                     // ...draw the hilight...
  235.                     if ( ShowDragHilite ( theDrag, tempRgn, true ) == noErr )
  236.                         // ... and remember it's now hilited
  237.                         bHasHilitedWindow = true;
  238.                     
  239.                     DisposeRgn ( tempRgn );
  240.                 }
  241.                 
  242.                 
  243.                 GetDragMouse ( theDrag, &theMouse, 0L );
  244.                 theOffset = HitTest ( theMouse, &hitDocument );
  245.                 
  246.                 if ( theDocument == hitDocument )
  247.                 {
  248.                 
  249.                     // Do not allow tracking through the selection in the
  250.                     // window that sourced the drag.
  251.                     if ( theAttributes & dragInsideSenderWindow )
  252.                         if ( IsOffsetInSelection ( theOffset, theDocument->theText ) )
  253.                             theOffset = -1;
  254.                         
  255.                     
  256.                     
  257.                     insertPosition = theOffset;
  258.                     
  259.                     //    Reset flashing counter if the offset has moved. This makes the
  260.                     //    caret blink only after the caret has stopped moving long enough.
  261.                     if ( theOffset != lastOffset )
  262.                     {
  263.                         caretMovedTime = theTime;
  264.                         bShowCaret = true;
  265.                     }
  266.                     lastOffset = theOffset;
  267.                     
  268.                     
  269.                     //    Flash caret.
  270.                     if ( theTime - caretMovedTime > LMGetCaretTime ( ) )
  271.                     {
  272.                         bShowCaret = !bShowCaret;
  273.                         caretMovedTime = theTime;
  274.                     }
  275.                     
  276.                     if ( !bShowCaret )
  277.                         theOffset = -1;
  278.                         
  279.                     //    If caret offset has changed, move caret on screen.
  280.                     if (theOffset != gCaretOffset)
  281.                     {
  282.                         if (gCaretOffset != -1)
  283.                             DrawCaret(gCaretOffset, theDocument->theText);
  284.                         
  285.                         if (theOffset != -1)
  286.                             DrawCaret(theOffset, theDocument->theText);
  287.                     }
  288.                     
  289.                     gCaretOffset = theOffset;
  290.                 }
  291.                 else
  292.                 {
  293.                     lastOffset = theOffset;
  294.                     insertPosition = -1;
  295.                 }
  296.  
  297.             }
  298.         break;
  299.         
  300.         case dragTrackingLeaveWindow:
  301.             //    If the caret is on the screen, remove it.
  302.             if (gCaretOffset != -1)
  303.             {
  304.                 DrawCaret(gCaretOffset, theDocument->theText);
  305.                 gCaretOffset = -1;
  306.             }
  307.             
  308.             // Remove any window hiliting here. If the mouse
  309.             // is not in the window and it's hilited...
  310.             if ( bHasHilitedWindow )
  311.                 // ...erase the hilight...
  312.                 if ( HideDragHilite ( theDrag ) == noErr )
  313.                     // ...remember that nothing is hilited
  314.                     bHasHilitedWindow = false;
  315.         break;
  316.         
  317.         case dragTrackingLeaveHandler:
  318.         break;
  319.         
  320.         default:
  321.             theErr = paramErr;
  322.         break;
  323.     }
  324.     
  325.     return theErr;
  326. }
  327.  
  328.  
  329.  
  330. Boolean IsOffsetInSelection ( short theOffset, TEHandle theTE )
  331. {
  332.     return (theOffset >= (*theTE)->selStart && theOffset <= (*theTE)->selEnd);
  333. }
  334.  
  335.  
  336.  
  337. //
  338. // DragIsNotInSourceWindow returns true if the drag in progress
  339. // is not in the same window it originated in. This is called by
  340. // the tracking and receive handlers.
  341. //
  342. static Boolean DragIsNotInSourceWindow ( DragReference theDrag )
  343. {
  344.     DragAttributes currDragFlags;
  345.     
  346.     GetDragAttributes ( theDrag, &currDragFlags );
  347.     return !(currDragFlags & dragInsideSenderWindow);
  348. }
  349.  
  350.  
  351.  
  352. //
  353. // DragReceiver is called by the drag manager whenever an
  354. // item is dropped on one of the application's windows.
  355. //
  356. pascal OSErr MyReceiveHandler ( WindowPtr theWindow, void* handlerRefCon, 
  357.                                     DragReference theDrag )
  358. {
  359. #ifdef __MWERKS__
  360.     #pragma unused(handlerRefCon)
  361. #endif
  362.  
  363.     Boolean                bHaveData = false;
  364.     Boolean                bMove = false;
  365.     OSErr                theErr = noErr;
  366.     
  367.     
  368.     unsigned short        numItems;
  369.     int                    i;
  370.     unsigned long        theAttributes;
  371.     ItemReference        theItem;
  372.     Size                textSize, styleSize;
  373.     DPtr                theDocument;
  374.     
  375.     short                theOffset;
  376.     Point                thePoint = { 0, 0 };
  377.     
  378.     Ptr                    textData = nil;
  379.     StScrpHandle        styleHandle = nil;
  380.  
  381.     TEHandle            tempTE = nil;
  382.     Rect                tempRect;
  383.  
  384.     
  385.     
  386.     RgnHandle            tempRgn;
  387.     Rect                sourceRect, targetRect;
  388.  
  389.     tWindowOffscreen*    theOffscreen = nil;
  390.     
  391.     if ( !(CanAcceptDragItems ( theDrag, theWindow ) && IsDragInWindowContent ( theDrag, theWindow )) )
  392.         return dragNotAcceptedErr;
  393.     
  394.     theDocument = DPtrFromWindowPtr ( theWindow );
  395.     GetDragAttributes ( theDrag, &theAttributes );
  396.     
  397.     // Get the location of the drop
  398.     theErr = GetDragMouse ( theDrag, &thePoint, 0L );
  399.     if ( theErr ) goto CleanupAndBail;
  400.     
  401.     GlobalToLocal ( &thePoint );            // Assumes theWindow is current port
  402.     // Map the drop location to the text
  403.     theOffset = TEGetOffset ( thePoint, theDocument->theText );
  404.  
  405.     // Don't allow a drop within the original selection
  406.     if ( theAttributes & dragInsideSenderWindow )
  407.         if ( IsOffsetInSelection ( theOffset, theDocument->theText ) )
  408.             return dragNotAcceptedErr;
  409.             
  410.     
  411.     // Is this drag copying or a moving the text?
  412.     bMove = IsDragMoving ( theDrag );
  413.     
  414.     // First, gather all the text into a temp TE record, and then insert that into
  415.     // the doucment. This approach makes it easy to handle things like text selection.
  416.     SetRect ( &tempRect, 0, 0, 0, 0 );
  417.     tempTE = TEStylNew ( &tempRect, &tempRect );
  418.     theErr = MemError ( );
  419.     if ( tempTE == nil || theErr ) goto CleanupAndBail;
  420.     
  421.     theErr = CountDragItems ( theDrag, &numItems );
  422.     if ( theErr ) goto CleanupAndBail;
  423.     
  424.     for ( i = 1; i <= numItems; i++ )
  425.     {
  426.         
  427.         theErr = GetDragItemReferenceNumber ( theDrag, i, &theItem );
  428.         if ( theErr ) goto CleanupAndBail;
  429.         
  430.         theErr = GetFlavorDataSize ( theDrag, theItem, 'TEXT', &textSize );
  431.         if ( theErr ) goto CleanupAndBail;
  432.         
  433.         textData = NewPtr ( textSize );
  434.         theErr = MemError ( );
  435.         if ( textData == nil || theErr ) goto CleanupAndBail;
  436.         
  437.         theErr = GetFlavorData ( theDrag, theItem, 'TEXT', textData, &textSize, 0L );
  438.         if ( theErr ) goto CleanupAndBail;
  439.         
  440.         
  441.         // Check for optional styl data for the TEXT.
  442.         styleHandle = 0L;
  443.         theErr = GetFlavorDataSize ( theDrag, theItem, 'styl', &styleSize );
  444.         if ( !theErr )
  445.         {
  446.             styleHandle = (StScrpHandle) NewHandle ( styleSize );
  447.             theErr = MemError ( );
  448.             if ( styleHandle == nil || theErr ) goto CleanupAndBail;
  449.             
  450.             HLock ( (Handle) styleHandle );
  451.             theErr = GetFlavorData ( theDrag, theItem, 'styl', *styleHandle, &styleSize, 0L );
  452.             if ( theErr ) goto CleanupAndBail;
  453.             HUnlock ( (Handle) styleHandle );
  454.         }
  455.         
  456.         // Insert this drag item's text into the tempTE.
  457.         TESetSelect ( 32767, 32767, tempTE );
  458.         TEStylInsert ( textData, textSize, styleHandle, tempTE );
  459.         
  460.         DisposePtr ( textData );
  461.         textData = nil;
  462.         if ( styleHandle )
  463.         {
  464.             DisposeHandle ( (Handle) styleHandle );
  465.             styleHandle = nil;
  466.         }
  467.         
  468.     }
  469.     
  470.     
  471.     // Pull the TEXT and styl data out of the tempTE handle.
  472.     textData = NewPtr (textSize = (**tempTE).teLength );
  473.     theErr = MemError ( );
  474.     if ( textData == nil || theErr ) goto CleanupAndBail;
  475.     
  476.     BlockMoveData ( *(*tempTE)->hText, textData, textSize );
  477.     TESetSelect ( 0, 32767, tempTE );
  478.     styleHandle = TEGetStyleScrapHandle (tempTE );
  479.     TEDispose ( tempTE );
  480.     tempTE = nil;
  481.     
  482.     
  483.     // Insert any text into the destination.
  484.     if ( textSize != 0 )
  485.     {
  486.         
  487.         // Get rid of the hilite/caret before inserting
  488.         if ( theAttributes & dragHasLeftSenderWindow )
  489.             HideDragHilite ( theDrag );
  490.         
  491.         if ( gCaretOffset != -1 )
  492.         {
  493.             DrawCaret ( gCaretOffset, theDocument->theText );
  494.             gCaretOffset = -1;
  495.         }
  496.         
  497.         //    If the drag occurred completely within the same window and the window is not
  498.         //    frontmost, bring the window forward and update its contents before completing
  499.         //    the drag.
  500.         if ( (theAttributes & dragInsideSenderWindow) && (theWindow != FrontWindow ( )) )
  501.         {
  502.             SelectWindow ( theWindow );
  503.             DoUpdate ( theWindow );
  504.             if (theDocument->windowType == kOrdinaryWind)
  505.                 TEActivate ( theDocument->theText );
  506.         }
  507.         
  508.         //    If the window is not active, must activate TE before inserting
  509.         //    text or the background hilite will not update correctly.
  510.         if ( !IsWindowHilited ( theWindow ) )
  511.             if (theDocument->windowType == kOrdinaryWind)
  512.                 TEActivate ( theDocument->theText );
  513.         
  514.         //    Draw everything into offscreen pixmap.
  515.         theOffscreen = DrawOffscreen ( theWindow );
  516.         if ( theOffscreen  )
  517.             (*theDocument->theText)->inPort = (GrafPtr) theOffscreen->offscreenWorld;
  518.         
  519.         if ( bMove )
  520.         {
  521.             // Get the current hilite rgn for zooming (source)
  522.             tempRgn = NewRgn ( );
  523.             theErr = MemError ( );
  524.             if ( theErr )    goto CleanupAndBail;
  525.             GetSelectedTextRgn ( theDocument, tempRgn );
  526.             sourceRect = (*tempRgn)->rgnBBox;
  527.             LocalRectToGlobalRect ( &sourceRect, theWindow );
  528.             
  529.             // If this is a move operation, delete the old text.
  530.             DeleteTextSelection ( theDocument->theText, &theOffset );
  531.         }
  532.         
  533.         InsertTextAtOffset ( theOffset, textData, textSize, styleHandle, theDocument->theText );
  534.         
  535.         // If the text is moving (not copying) within the same window, provide a ZoomRects
  536.         // from the source to the destination before revealing the reflowed text.
  537.         
  538.         if ( bMove )
  539.         {
  540.             // Get the current hilite rgn for zooming (target)
  541.             GetSelectedTextRgn ( theDocument, tempRgn );
  542.             targetRect = (*tempRgn)->rgnBBox;
  543.             DisposeRgn ( tempRgn );
  544.             tempRgn = nil;
  545.             LocalRectToGlobalRect ( &targetRect, theWindow );
  546.             
  547.             ZoomRects ( &sourceRect, &targetRect, 12, zoomDecelerate );
  548.         }
  549.     }
  550.     
  551.     // Undo the TEActivate, if needed.
  552.     if ( !IsWindowHilited ( theWindow ) )
  553.         TEDeactivate ( theDocument->theText );
  554.     
  555.     //    Show the offscreen bitmap.
  556.     if ( theOffscreen )
  557.     {
  558.         theOffscreen = DrawOnscreen ( theOffscreen );
  559.         (*theDocument->theText)->inPort = theWindow;
  560.     }
  561.     
  562.     // Make the document dirty
  563.     theDocument->dirty = true;
  564.     
  565.     
  566. CleanupAndBail:
  567.     
  568.     
  569.     // All of these will be nil if no error occurred
  570.     if ( textData )
  571.         DisposePtr ( textData );
  572.     if ( styleHandle )
  573.         DisposeHandle ( (Handle) styleHandle );
  574.     if ( tempTE )
  575.         TEDispose ( tempTE );
  576.     if ( tempRgn )
  577.         DisposeRgn ( tempRgn );
  578.     
  579.     // Normally  nil since DrawOnscreen calls DisposeOffscreen
  580.     if ( theOffscreen )
  581.     {
  582.         DisposeOffscreen ( theOffscreen );
  583.         (*theDocument->theText)->inPort = theWindow;
  584.     }
  585.     
  586.     
  587.     return theErr;
  588. }
  589.  
  590.  
  591.  
  592. void DeleteTextSelection ( TEHandle theTE, short* theInsertPosition )
  593. {
  594.     short selStart, selEnd;
  595.     
  596.     selStart = (*theTE)->selStart;
  597.     selEnd = (*theTE)->selEnd;
  598.     if ( WhiteSpaceAtOffset ( selStart - 1, theTE ) &&
  599.             !WhiteSpaceAtOffset ( selStart, theTE ) &&
  600.             !WhiteSpaceAtOffset ( selEnd - 1, theTE ) &&
  601.              WhiteSpaceAtOffset ( selEnd, theTE ) )
  602.     {
  603.         
  604.         if ( GetCharAtOffset ( selEnd, theTE ) == ' ' )
  605.             (*theTE)->selEnd++;
  606.     }
  607.     
  608.     if ( *theInsertPosition > selStart )
  609.         *theInsertPosition -= ((*theTE)->selEnd - (*theTE)->selStart);
  610.     
  611.     TEDelete ( theTE );
  612.     
  613.     return;
  614. }
  615.  
  616.  
  617.  
  618. static Boolean IsDragMoving ( DragReference theDrag )
  619. {
  620.     DragAttributes    theAttributes;
  621.     
  622.     GetDragAttributes ( theDrag, &theAttributes );
  623.     return (theAttributes & dragInsideSenderWindow) && !IsDragWithOptionKey ( theDrag );
  624. }
  625.  
  626.  
  627.  
  628. static Boolean IsDragWithOptionKey ( DragReference theDrag )
  629. {
  630.     short    mouseDownModifiers, mouseUpModifiers;
  631.     
  632.     GetDragModifiers ( theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers );
  633.     return (mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey);
  634. }
  635.  
  636.  
  637.  
  638. void OutlineRegion ( RgnHandle theRgn )
  639. {
  640.     RgnHandle tempRgn;
  641.     
  642.     tempRgn = NewRgn ( );
  643.     CopyRgn ( theRgn, tempRgn );
  644.     InsetRgn ( tempRgn, 1, 1 );
  645.     DiffRgn ( theRgn, tempRgn, theRgn );
  646.     DisposeRgn ( tempRgn );
  647.     
  648.     return;
  649. }
  650.  
  651.  
  652.  
  653. OSErr DoWindowContentDrag ( WindowPtr theWindow, EventRecord* theEvent )
  654. {
  655.     OSErr            theErr = noErr;
  656.     DragReference    theDrag = (unsigned long) nil;
  657.     RgnHandle        dragRgn = nil;
  658.     Ptr                dataPtr = nil;
  659.     StScrpHandle    styleHandle = nil;
  660.     short            dataSize;
  661.     
  662.     DPtr            theDocument;
  663.     Rect            dragBounds;
  664.     ItemReference    theItem;
  665.     
  666.     
  667.     // create a new drag
  668.     theErr = NewDrag ( &theDrag );
  669.     if ( theErr ) goto CleanupAndBail;
  670.     
  671.     // use the window ptr as item reference for the heck of it
  672.     theItem = (ItemReference) theWindow;
  673.     
  674.     // add the data to the drag
  675.     theDocument = DPtrFromWindowPtr ( theWindow );
  676.     dataPtr = NewPtr ( 0L );
  677.     theErr = GetSelectedText ( theDocument, dataPtr, &dataSize );
  678.     if ( theErr || dataSize == 0 )    goto CleanupAndBail;
  679.     
  680.     theErr = AddDragItemFlavor ( theDrag, theItem, 'TEXT', dataPtr, dataSize, 0 );
  681.     if ( theErr )    goto CleanupAndBail;
  682.     DisposePtr ( dataPtr );
  683.     dataPtr = nil;
  684.     
  685.     
  686.     // Add style data
  687.     styleHandle = TEGetStyleScrapHandle ( theDocument->theText );
  688.     HLock ( (Handle) styleHandle );
  689.     AddDragItemFlavor ( theDrag, theItem, 'styl', (Ptr) *styleHandle, GetHandleSize ( (Handle) styleHandle ), 0 );
  690.     HUnlock ( (Handle) styleHandle );
  691.     DisposeHandle ( (Handle) styleHandle );
  692.     styleHandle = nil;
  693.     
  694.     
  695.     // generate the bounds and region for the drag using the window's
  696.     // content rectangle
  697.     dragBounds = (**((WindowPeek) theWindow)->contRgn).rgnBBox;
  698.     theErr = SetDragItemBounds(theDrag, theItem, &dragBounds);
  699.     if ( theErr ) goto CleanupAndBail;
  700.     
  701.     dragRgn = NewRgn ( );
  702.     GetSelectedTextRgn ( theDocument, dragRgn );
  703.     LocalRgnToGlobalRgn ( dragRgn, nil );
  704.     OutlineRegion ( dragRgn );
  705.     
  706.     // do the drag and clean up
  707.     TrackDrag ( theDrag, theEvent, dragRgn );
  708.     
  709.     
  710.     if ( DragIsNotInSourceWindow ( theDrag ) )
  711.     {
  712.         AEDesc    dropLocation;
  713.         
  714.         // Get the drop location
  715.         GetDropLocation ( theDrag, &dropLocation );
  716.         if ( !IsDragWithOptionKey ( theDrag ) && DropLocationIsFinderTrash ( &dropLocation) )
  717.         {
  718.             // Delete the exact text. Don't call DeleteTextSelection
  719.             TEDelete ( theDocument->theText );
  720.             theDocument->dirty = true;
  721.         }
  722.         
  723.         AEDisposeDesc ( &dropLocation );
  724.     }
  725.     
  726.     
  727. CleanupAndBail:
  728.     
  729.     if ( theDrag )
  730.         DisposeDrag(theDrag);
  731.     
  732.     if ( dragRgn )
  733.         DisposeRgn ( dragRgn );
  734.     
  735.     // These should be nil
  736.     if ( dataPtr )
  737.         DisposePtr ( dataPtr );
  738.     if ( styleHandle )
  739.         DisposeHandle ( (Handle) styleHandle );
  740.     
  741.     
  742.     return theErr;
  743. }
  744.  
  745.  
  746.  
  747. void LocalRgnToGlobalRgn ( RgnHandle theRgn, WindowRef theWindow )
  748. {
  749.     GrafPtr        savePort;
  750.     Point        localPt, globalPt;
  751.     
  752.     
  753.     if ( theWindow )
  754.     {
  755.         GetPort ( &savePort );
  756.         SetPortWindowPort ( theWindow );
  757.     }
  758.     
  759.     localPt = globalPt = *(Point*) &(*theRgn)->rgnBBox;        // top left
  760.     LocalToGlobal ( &globalPt );
  761.     SubPt ( localPt, &globalPt );
  762.     OffsetRgn ( theRgn, globalPt.h, globalPt.v );
  763.     
  764.     if ( theWindow )
  765.         SetPort ( savePort );
  766.         
  767.     return;
  768. }
  769.  
  770.  
  771.  
  772. void LocalRectToGlobalRect ( Rect* theRect, WindowRef theWindow )
  773. {
  774.     GrafPtr        savePort;
  775.     Point        localPt, globalPt;
  776.     
  777.     
  778.     if ( theWindow )
  779.     {
  780.         GetPort ( &savePort );
  781.         SetPortWindowPort ( theWindow );
  782.     }
  783.     
  784.     localPt = globalPt = *(Point*) theRect;        // top left
  785.     LocalToGlobal ( &globalPt );
  786.     SubPt ( localPt, &globalPt );
  787.     OffsetRect ( theRect, globalPt.h, globalPt.v );
  788.     
  789.     if ( theWindow )
  790.         SetPort ( savePort );
  791.         
  792.     return;
  793. }
  794.  
  795.  
  796.  
  797.  
  798. OSErr GetSelectedText ( DPtr theDocument, Ptr dataPtr, short* dataSize )
  799. {
  800.     OSErr theErr;
  801.     TEHandle teHandle = theDocument->theText;
  802.     
  803.     *dataSize = (**(teHandle)).selEnd - (**(teHandle)).selStart;
  804.     if ( *dataSize )
  805.     {
  806.         SetPtrSize ( dataPtr, *dataSize );
  807.         theErr = MemError ( );
  808.         if ( theErr )
  809.             return theErr;
  810.         BlockMoveData ( *(**(teHandle)).hText + (**(teHandle)).selStart, dataPtr, *dataSize );
  811.     }
  812.     
  813.     return noErr;
  814. }
  815.  
  816.  
  817.  
  818. void GetSelectedTextRgn ( DPtr theDocument, RgnHandle dataRgn )
  819. {
  820.     TEGetHiliteRgn ( dataRgn, theDocument->theText );
  821.     
  822.     return;
  823. }
  824.  
  825.  
  826.  
  827. //
  828. // Does the user want to drag something? First, checks the click could
  829. // be a drag, then waits to see if the user starts to drag the text.
  830. // 
  831. Boolean UserWantsToDrag ( WindowRef theWindow, Point globalPt )
  832. {
  833.     Point    localPt;
  834.     
  835.     localPt = globalPt;
  836.     GlobalToLocal ( &localPt );            // Assumes theWindow is current port
  837.     if ( PointInWindowSelection ( localPt, theWindow ) )
  838.         return WaitMouseMoved ( globalPt );
  839.         
  840.     return false;
  841. }
  842.  
  843.  
  844.  
  845. //
  846. // Returns true if the local point is in the window's current selection
  847. //
  848. Boolean PointInWindowSelection ( Point localPt, WindowRef theWindow )
  849. {
  850.     Boolean        bHit;
  851.     RgnHandle    tempRgn;
  852.     DPtr        theDocument;
  853.     
  854.     theDocument = DPtrFromWindowPtr ( theWindow );
  855.     if ( !(PtInDocument ( localPt, theDocument )) )
  856.         return false;
  857.     
  858.     tempRgn = NewRgn ( );
  859.     GetSelectedTextRgn ( theDocument, tempRgn );
  860.     bHit = PtInRgn ( localPt, tempRgn );
  861.     DisposeRgn ( tempRgn );
  862.     
  863.     return bHit;
  864. }
  865.  
  866.  
  867.  
  868. //
  869. //    Given a point in global coordinates, HitTest returns a pointer to a
  870. //    document structure if the point is inside a document window on the screen.
  871. //    If the point is not inside a document window, HitTest return NULL in
  872. //    theDoc. If the point is in a doument window and also in the viewRect of
  873. //    the document's TextEdit field, HitTest also returns the offset into
  874. //    the text that corresponds to that point. If the point is not in the text,
  875. //    HitTest returns -1.
  876. //
  877. short HitTest(Point theLoc, DPtr* theDoc)
  878. {
  879.     GrafPtr        savePort;
  880.     WindowPtr    theWindow;
  881.     short        offset;
  882.     
  883.     *theDoc = 0L;
  884.     offset = -1;
  885.     
  886.     if (FindWindow(theLoc, &theWindow) == inContent)
  887.     {
  888.         if ( Ours ( theWindow ) )
  889.         {
  890.             *theDoc = DPtrFromWindowPtr ( theWindow );
  891.             GetPort ( &savePort );
  892.             SetPort(theWindow);
  893.             GlobalToLocal(&theLoc);
  894.             SetPort ( savePort );
  895.             
  896.             if ((PtInRect(theLoc, &(**((**theDoc).theText)).viewRect)) && 
  897.                 (PtInRect(theLoc, &(**((**theDoc).theText)).destRect))) {
  898.                 
  899.                 offset = TEGetOffset(theLoc, (**theDoc).theText);
  900.  
  901.                 if ((TEIsFrontOfLine(offset, (**theDoc).theText)) && (offset) &&            
  902.                         ((*((**((**theDoc).theText)).hText))[offset - 1] != 0x0D) &&
  903.                         (TEGetPoint(offset - 1, (**theDoc).theText).h < theLoc.h)) {
  904.                     offset--;
  905.                 }
  906.             }
  907.         }
  908.     }
  909.  
  910.     return(offset);
  911. }
  912.  
  913.  
  914.  
  915. // TEIsFrontOfLine, given an offset and a TextEdit handle, returns true if
  916. // the given offset is at the beginning of a line start.
  917. short TEIsFrontOfLine ( short offset, TEHandle theTE )
  918.  
  919. {    short        line = 0;
  920.  
  921.     if ((**theTE).teLength == 0)
  922.         return(true);
  923.  
  924.     if (offset >= (**theTE).teLength)
  925.         return( (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0d );
  926.  
  927.     while ((**theTE).lineStarts[line] < offset)
  928.         line++;
  929.  
  930.     return( (**theTE).lineStarts[line] == offset );
  931. }
  932.  
  933.  
  934.  
  935. // TEGetLine, given an offset and a TextEdit handle, returns the line number
  936. // of the line that contains the offset.
  937. short TEGetLine ( short offset, TEHandle theTE )
  938.  
  939. {    short        line = 0;
  940.  
  941.     if (offset > (**theTE).teLength)
  942.         return((**theTE).nLines);
  943.  
  944.     while ((**theTE).lineStarts[line] < offset)
  945.         line++;
  946.     
  947.     return(line);
  948. }
  949.  
  950.  
  951.  
  952. //
  953. //    DrawCaret draws a caret in a TextEdit field at the given offset. DrawCaret
  954. //    expects the port to be set to the port that the TextEdit field is in.
  955. //    DrawCaret inverts the image of the caret onto the screen.
  956. //
  957. void DrawCaret(short offset, TEHandle theTE)
  958.  
  959. {    Point        theLoc;
  960.     short        theLine, lineHeight;
  961.  
  962.  
  963.     // Get the coordinates and the line of the offset to draw the caret.
  964.  
  965.     theLoc  = TEGetPoint(offset, theTE);
  966.     theLine = TEGetLine(offset, theTE);
  967.     
  968.     
  969.     // For some reason, TextEdit dosen't return the proper coordinates
  970.     // of the last offset in the field if the last character in the record
  971.     // is a carriage return. TEGetPoint returns a point that is one line
  972.     // higher than expected. The following code fixes this problem.
  973.     if ((offset == (**theTE).teLength) &&
  974.             (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0D) {
  975.         theLoc.v += TEGetHeight(theLine, theLine, theTE);
  976.     }
  977.  
  978.     // Always invert the caret when drawing.
  979.     PenMode(patXor);
  980.  
  981.     //Get the height of the line that the offset points to.
  982.     lineHeight = TEGetHeight(theLine, theLine, theTE);
  983.  
  984.     // Draw the appropriate caret image.
  985.     MoveTo(theLoc.h - 1, theLoc.v - 1);
  986.     Line(0, 1 - lineHeight);
  987.  
  988.     PenNormal();
  989. }
  990.  
  991.  
  992.  
  993. char GetCharAtOffset(short offset, TEHandle theTE)
  994.  
  995. {
  996.     if (offset < 0)
  997.         return(0x0D);
  998.  
  999.     return(((char *) *((**theTE).hText))[offset]);
  1000. }
  1001.  
  1002.  
  1003.  
  1004. Boolean WhiteSpace(char theChar)
  1005.  
  1006. {
  1007.     return((theChar == ' ') || (theChar == 0x0D));
  1008. }
  1009.  
  1010.  
  1011.  
  1012. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE)
  1013.  
  1014. {    char        theChar;
  1015.  
  1016.     if ((offset < 0) || (offset > (**theTE).teLength - 1))
  1017.         return(true);
  1018.  
  1019.     theChar = ((char *) *((**theTE).hText))[offset];
  1020.     return((theChar == ' ') || (theChar == 0x0D));
  1021. }
  1022.  
  1023.  
  1024.  
  1025. void InsertTextAtOffset(short offset, char *theBuf, long size, StScrpHandle theStyl, TEHandle theTE)
  1026.  
  1027. {
  1028.     if (size == 0)
  1029.         return;
  1030.  
  1031.     // If inserting at the end of a word and the selection does not begin with
  1032.     // a space, insert a space before the insertion.
  1033.     if (!WhiteSpaceAtOffset(offset - 1, theTE) &&
  1034.          WhiteSpaceAtOffset(offset, theTE) &&
  1035.         !WhiteSpace(theBuf[0])) {
  1036.  
  1037.         TESetSelect(offset, offset, theTE);
  1038.         TEKey(' ', theTE);
  1039.         offset++;
  1040.     }
  1041.  
  1042.     //    If inserting at the beginning of a word and the selection does not end
  1043.     //    with a space, insert a space after the insertion.
  1044.     if ( WhiteSpaceAtOffset(offset - 1, theTE) &&
  1045.         !WhiteSpaceAtOffset(offset, theTE) &&
  1046.         !WhiteSpace(theBuf[size - 1])) {
  1047.  
  1048.         TESetSelect(offset, offset, theTE);
  1049.         TEKey(' ', theTE);
  1050.     }
  1051.     
  1052.     TESetSelect(offset, offset, theTE);
  1053.     TEStylInsert(theBuf, size, theStyl, theTE);
  1054.     TESetSelect(offset, offset + size, theTE);
  1055.     
  1056.     return;
  1057. }
  1058.  
  1059.  
  1060.  
  1061. //
  1062. // DropLocationIsFinderTrash returns true if the given dropLocation
  1063. // AEDesc is a descriptor of the Finder's Trash.
  1064. //
  1065. Boolean DropLocationIsFinderTrash ( AEDesc* dropLocation )
  1066.  
  1067. {    OSErr            result;
  1068.     AEDesc            dropSpec;
  1069.     FSSpecPtr        theSpec;
  1070.     CInfoPBRec        thePB;
  1071.     short            trashVRefNum;
  1072.     long            trashDirID;
  1073.     
  1074.     
  1075.     
  1076.     //    Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or
  1077.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  1078.     if ( (dropLocation->descriptorType != typeNull) &&
  1079.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr))
  1080.     {
  1081.  
  1082.         HLock(dropSpec.dataHandle);
  1083.         theSpec = (FSSpec *) *dropSpec.dataHandle;
  1084.  
  1085.         //    Get the directory ID of the given dropLocation object.
  1086.         thePB.dirInfo.ioCompletion = 0L;
  1087.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  1088.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  1089.         thePB.dirInfo.ioFDirIndex = 0;
  1090.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  1091.         
  1092.         result = PBGetCatInfo(&thePB, false);
  1093.  
  1094.         HUnlock(dropSpec.dataHandle);
  1095.         AEDisposeDesc(&dropSpec);
  1096.         
  1097.         if ( result )
  1098.             return(false);
  1099.  
  1100.         //    If the result is not a directory, it cannot be the Trash.
  1101.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  1102.             return(false);
  1103.  
  1104.         //    Get information about the Trash folder.
  1105.         FindFolder ( theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
  1106.  
  1107.         //    If the directory ID of the dropLocation object is the same as the directory ID
  1108.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  1109.         if ( thePB.dirInfo.ioDrDirID == trashDirID )
  1110.             return true;
  1111.     }
  1112.     
  1113.     
  1114.     return false;
  1115. }
  1116.  
  1117.